<!-- menu的处理 -->
<template>
  <ul :class="menuClasses">
    <slot></slot>
  </ul>
</template>

<script>
import Emitter from '../../../mixins/emitter'
import size from '../../../mixins/size'
import {
  oneOf,
  findComponentsDownward,
  findComponentsUpward
} from '../../../utils/assist'

export default {
  name: 'Menu',
  mixins: [Emitter, size],
  components: {},
  props: {
    mode: {
      validator (value) {
        return oneOf(value, ['hor', 'ver', 'ver-show'])
      },
      default: 'ver'
    },
    activeName: {
      // 活跃的名字，命名的名字可以使用name
      type: [String, Number]
    },
    openNames: {
      type: Array,
      default () {
        return []
      }
    },
    accordion: {
      // 是否只保持一个子菜单的展开
      type: Boolean,
      default: true
    },
    collapse: {
      // 是否折叠，在垂直的时候使用
      type: Boolean,
      default: false
    },
    router: {
      // 默认使用name来作为跳转路径
      type: Boolean,
      default: false
    },
    expand: {
      // 菜单展开方式
      type: String,
      default: 'click',
      validator (value) {
        return oneOf(value, ['click', 'hover'])
      }
    }
  },
  data () {
    // 这里存放数据
    return {
      currentActiveName: this.activeName,
      openedNames: [],
      items: {},
      submenus: {}
    }
  },
  // 监听属性 类似于data概念
  computed: {
    menuClasses () {
      return [
        `${'sh-menu'}`,
        {
          [`sh-menu-${this.mode}`]: this.mode,
          [`sh-menu-collapse`]: this.collapse && this.mode !== 'hor',
          [`sh-menu-${this.sizes}`]: !!this.sizes
        }
      ]
    },
    isMenuPopup () {
      return this.mode === 'hor' || (this.mode === 'ver' && this.collapse)
    }
  },
  // 监控data中的数据变化
  watch: {
    openNames (names) {
      this.openedNames = names
    },
    activeName (val) {
      this.currentActiveName = val
      this.updateActiveIndex(val)
    },
    currentActiveName () {
      this.updateActiveName()
    },
    collapse (value) {
      if (value) this.openedNames = []
      this.broadcast('Submenu', 'toggle-collapse', value)
    }
  },
  // 方法集合
  methods: {
    updateActiveIndex (val) {
      const item = this.items[val]
      if (item) {
        if (this.accordion) {
          findComponentsDownward(this, 'Submenu').forEach(item => {
            item.opened = false
          })
        }
        findComponentsUpward(item, 'Submenu').forEach(item => {
          item.opened = true
        })
        let openedNames = findComponentsDownward(this, 'Submenu')
          .filter(item => item.opened)
          .map(item => item.name)
        this.openedNames = [...openedNames]
        this.$emit('on-open-change', openedNames)

        // 获得name 和 item
        let label = ''
        if (item.$slots.default) {
          label = item.$slots.default[0].text
        }
        if (item.$slots.title) {
          item.$slots.title.forEach(item => {
            if (item.tag === 'span') {
              label = item.children[0].text
            }
          })
        }
        let dict = {
          path: item.name,
          name: item.name,
          label: label
        }
        this.$emit('on-active-name', dict)
      }
    },
    addItem (item) {
      this.$set(this.items, item.name, item)
    },
    removeItem (item) {
      delete this.items[item.name]
    },
    addSubmenu (item) {
      this.$set(this.submenus, item.name, item)
    },
    removeSubmenu (item) {
      delete this.submenus[item.name]
    },
    updateActiveName () {
      if (this.currentActiveName === undefined) {
        this.currentActiveName = -1
      }
      this.broadcast('Submenu', 'on-update-active-name', {
        name: this.currentActiveName,
        status: false
      })
      this.broadcast(
        'MenuItem',
        'on-update-active-name',
        this.currentActiveName
      )
      this.broadcast('Submenu', 'on-update-active-children', {
        name: this.currentActiveName,
        status: false
      })
    },
    initOpenedMenu () {
      // 初始化展开菜单
      const items = findComponentsDownward(this, 'Submenu')
      if (this.mode === 'ver-show') {
        if (items.length) {
          this.openedNames = []
          items.forEach(item => {
            this.openedNames.push(item.name)
            item.opened = true
          })
        }
      } else {
        if (items.length) {
          items.forEach(item => {
            if (this.openedNames.indexOf(item.name) > -1) item.opened = true
            else item.opened = false
          })
        }
      }
    },
    updateOpenKeys (name) {
      let names = [...this.openedNames]
      const index = names.indexOf(name)
      // 核心方法
      if (this.mode === 'ver-show') {
        // 忽略掉全部展开模式
      } else {
        if (this.accordion) {
          findComponentsDownward(this, 'Submenu').forEach(item => {
            item.opened = false
          })
        }
        if (index >= 0) {
          let currentSubmenu = null
          findComponentsDownward(this, 'Submenu').forEach(item => {
            if (item.name === name) {
              currentSubmenu = item
              item.opened = false
            }
          })
          findComponentsUpward(currentSubmenu, 'Submenu').forEach(item => {
            item.opened = true
          })
          findComponentsDownward(currentSubmenu, 'Submenu').forEach(item => {
            item.opened = false
          })
        } else {
          if (this.accordion) {
            let currentSubmenu = null
            findComponentsDownward(this, 'Submenu').forEach(item => {
              if (item.name === name) {
                currentSubmenu = item
                item.opened = true
              }
            })
            findComponentsUpward(currentSubmenu, 'Submenu').forEach(item => {
              item.opened = true
            })
          } else {
            findComponentsDownward(this, 'Submenu').forEach(item => {
              if (item.name === name) item.opened = true
            })
          }
        }
        let openedNames = findComponentsDownward(this, 'Submenu')
          .filter(item => item.opened)
          .map(item => item.name)
        this.openedNames = [...openedNames]
        this.$emit('on-open-change', openedNames)
      }
    },
    handleEmitSelectEvent (name) {
      this.$emit('on-select', name)
    },
    routeToItem (item, onError) {
      let route = item.route || item.name
      try {
        this.$router.push(route, () => {}, onError)
      } catch (e) {
        console.error(e)
      }
    },
    handleItemClick (item) {
      let label = ''
      if (item.$slots.default) {
        label = item.$slots.default[0].text
      }
      if (item.$slots.title) {
        item.$slots.title.forEach(item => {
          if (item.tag === 'span') {
            label = item.children[0].text
          }
        })
      }
      let dict = {
        path: item.name,
        name: item.name,
        label: label
      }
      this.$emit('on-select', dict)
      const hasIndex = item.name !== null
      if (this.router && hasIndex) {
        this.routeToItem(item, error => {
          if (error) console.error(error)
        })
      }
    }
  },
  mounted () {
    this.openedNames = [...this.openNames]
    this.initOpenedMenu()
    this.updateActiveIndex(this.currentActiveName)
    this.$nextTick(() => this.updateActiveName())
    this.$on('on-menu-item-select', name => {
      this.currentActiveName = name
    })
    this.$on('on-menu-item-click', this.handleItemClick)
  }
}
</script>
